package com.ruoyi.system.service.impl;

import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.*;
import com.ruoyi.system.domain.dto.OfflinePackageInfo;
import com.ruoyi.system.domain.vo.Packages;
import com.ruoyi.system.domain.vo.TaskMessage;
import com.ruoyi.system.mapper.*;
import com.ruoyi.system.service.OfflinePackageService;
import com.ruoyi.system.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Collections;
import java.util.Date;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Slf4j
@Service
public class OfflinePackageServiceImpl implements OfflinePackageService {

    @Value("${file.storage.offline}")
    private String fileStoragePath;

    @Resource
    private TbOfflineTaskDetailMapper offlineTaskDetailMapper;

    @Resource
    private TbOfflineTaskMapper offlineTaskMapper;

    @Resource
    private TbOfflineTaskTargetMapper offlineTaskTargetMapper;

    @Resource
    private TbAppVersionMapper appVersionMapper;

    @Resource
    private TbDataPackageMapper dataPackageMapper;



    private static final String OTA_FILENAME = "ota.dat";
    private static final Long VIRTUAL_DEVICE_ID = 0L;
    //任务初始化
    private static final int TASK_STATUS_INIT = 1;
    //待执行
    private static final int DETAIL_STATUS_PENDING = 0;
    //待执行
    private static final int TARGET_STATUS_PENDING = 0;

    @Override
    public File generateOfflinePackage(String packageVersionCode, String packageVersionNumber, Long packageId) throws IOException {
        Long virtualTaskId = generateVirtualTaskId();
        File zipFile = null;
        File tempSourceFile = null;

        try {
            // 1. 获取包信息
            OfflinePackageInfo packageInfo = getOfflinePackageInfo(packageVersionNumber, packageVersionCode, packageId);
            if (packageInfo == null) {
                throw new RuntimeException("包信息不存在");
            }
            // 2. 创建虚拟任务记录
            createVirtualTaskRecord(virtualTaskId, packageInfo);

            // 3. 获取源文件
            File sourceFile = getSourceFile(packageInfo.getFileUrl());
            tempSourceFile = sourceFile; // 标记为需要清理的临时文件

            // 4. 构建TaskMessage
            TaskMessage taskMessage = buildTaskMessage(virtualTaskId, packageInfo, sourceFile);

            // 5. 生成JSON文件内容
            String jsonContent = JsonUtils.toJsonString(taskMessage);

            // 6. 创建ZIP文件
            zipFile = createZipFile(virtualTaskId, jsonContent, sourceFile, packageInfo.getFileName());

            return zipFile;
        } catch (Exception e) {
            log.error("生成离线包失败, packageVersionCode: {}, packageId: {}", packageVersionCode, packageId, e);
            // 清理临时文件
            cleanupTempFile(tempSourceFile);
            // 回滚事务：删除已插入的虚拟任务记录
            rollbackVirtualTask(virtualTaskId);
            throw new RuntimeException("生成离线包失败: " + e.getMessage(), e);
        }
    }

    /**
     * 生成虚拟任务ID
     */
    private Long generateVirtualTaskId() {
        //return System.currentTimeMillis();
        return 0L;
    }

    /**
     * 获取离线包信息
     */
    private OfflinePackageInfo getOfflinePackageInfo(String packageVersionNumber,
                                                     String packageVersionCode,
                                                     Long packageId) {
        try {
            // 1. 首先尝试从应用版本表获取
            TbAppVersion appVersion = appVersionMapper.selectTbAppVersionByIdAndVersionCodeAndNumber(
                    packageId, packageVersionCode, packageVersionNumber);

            if (appVersion != null) {
                return convertToPackageInfo(appVersion);
            }

            // 2. 如果应用版本不存在，尝试从数据包表获取
            TbDataPackage dataPackage = dataPackageMapper.selectTbDataPackageByIdAndVersionCodeAndNumber(
                    packageId, packageVersionCode, packageVersionNumber);

            if (dataPackage != null) {
                return convertToPackageInfo(dataPackage);
            }

            // 3. 两个表中都未找到对应记录
            log.warn("未找到对应的离线包信息，packageId: {}, versionCode: {}, versionNumber: {}",
                    packageId, packageVersionCode, packageVersionNumber);
            return null;

        } catch (Exception e) {
            log.error("获取离线包信息失败，packageId: {}, versionCode: {}, versionNumber: {}",
                    packageId, packageVersionCode, packageVersionNumber, e);
            return null;
        }
    }

    /**
     * 转换TbAppVersion为OfflinePackageInfo
     */
    private OfflinePackageInfo convertToPackageInfo(TbAppVersion appVersion) {
        if (appVersion == null) return null;

        OfflinePackageInfo info = new OfflinePackageInfo();
        info.setPackageId(appVersion.getId());
        info.setFileName(extractFileNameFromUrl(appVersion.getPackageUrl()));
        info.setFileUrl(appVersion.getPackageUrl());
        info.setFileHash(appVersion.getPackageHash());
        info.setAppName(appVersion.getPackageName());
        info.setVersionCode(appVersion.getPackageVersioncode());
        info.setVersionNumber(appVersion.getPackageVersionnumber());
        info.setPackageType(1); // APP
        info.setPackageSize(appVersion.getPackageSize());
        return info;
    }

    /**
     * 转换TbDataPackage为OfflinePackageInfo
     */
    private OfflinePackageInfo convertToPackageInfo(TbDataPackage dataPackage) {
        if (dataPackage == null) return null;

        OfflinePackageInfo info = new OfflinePackageInfo();
        info.setPackageId(dataPackage.getId());
        info.setFileName(extractFileNameFromUrl(dataPackage.getPackageUrl()));
        info.setFileUrl(dataPackage.getPackageUrl());
        info.setFileHash(dataPackage.getPackageHash());
        info.setAppName(dataPackage.getPackageName());
        info.setVersionCode(dataPackage.getPackageVersioncode());
        info.setVersionNumber(dataPackage.getPackageVersionnumber());
        info.setPackageType(3); // ZIP数据包
        info.setPackageSize(dataPackage.getPackageSize());
        return info;
    }

    /**
     * 创建虚拟任务记录
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void createVirtualTaskRecord(Long taskId, OfflinePackageInfo packageInfo) {
        // 创建主任务记录
        TbOfflineTask task = new TbOfflineTask();
        task.setId(taskId);
        task.setTaskName("离线下载任务-" + packageInfo.getAppName());
        task.setTaskStatus(TASK_STATUS_INIT);
        task.setRegistrationTime(new Date());
        task.setTaskStartTime(new Date());
        task.setTaskStopTime(new Date());
        task.setTaskTargetDesc("离线包下载任务");
        task.setTaskProgress(0.0);
        offlineTaskMapper.insertTbOfflineTask(task);

        // 创建任务详情记录
        TbOfflineTaskDetail detail = new TbOfflineTaskDetail();
        detail.setId(generateVirtualTaskId());
        detail.setTaskId(taskId);
        detail.setTaskType(packageInfo.getPackageType() == 1 ? 3 : 1); // 应用安装或数据推送
        detail.setPushPackageType(packageInfo.getPackageType() == 1 ? 1 : 2); // app或data_package
        detail.setPackageId(packageInfo.getPackageId());
        detail.setPackageName(packageInfo.getAppName());
        detail.setAppName(packageInfo.getAppName());
        detail.setPackageVersioncode(packageInfo.getVersionCode());
        detail.setPackageVersionnumber(packageInfo.getVersionNumber());
        detail.setPackageSize(packageInfo.getPackageSize());
        detail.setStatus(DETAIL_STATUS_PENDING);
        detail.setStartTime(new Date());
        detail.setEndTime(new Date());
        detail.setProgress(0.0);
        detail.setDeviceCount(1);
        detail.setSuccessCount(0);
        detail.setFailCount(0);

        offlineTaskDetailMapper.insertTbOfflineTaskDetail(detail);

        // 创建任务目标记录（模拟一个设备）
        TbOfflineTaskTarget target = new TbOfflineTaskTarget();
        target.setId(generateVirtualTaskId());
        target.setTaskDetailId(detail.getId());
        target.setDeviceId(VIRTUAL_DEVICE_ID);
        target.setStatus(TARGET_STATUS_PENDING);
        target.setStartTime(new Date());
        target.setEndTime(new Date());
        target.setProgress(0.0);

        offlineTaskTargetMapper.insertTbOfflineTaskTarget(target);
    }

    /**
     * 回滚虚拟任务
     */
    private void rollbackVirtualTask(Long taskId) {
        try {
            if (taskId != null && taskId > 0) {
                offlineTaskMapper.deleteTbOfflineTaskById(taskId);
                // 同时删除相关的详情和目标记录
                offlineTaskDetailMapper.deleteTbOfflineTaskDetailById(taskId);
                offlineTaskTargetMapper.deleteTbOfflineTaskTargetById(taskId);
            }
        } catch (Exception e) {
            log.warn("回滚虚拟任务记录失败, taskId: {}", taskId, e);
        }
    }

    /**
     * 构建TaskMessage
     */
    private TaskMessage buildTaskMessage(Long taskId, OfflinePackageInfo packageInfo, File sourceFile) {
        Packages pkg = new Packages();
        pkg.setAppname(packageInfo.getAppName());
        pkg.setAppversion(packageInfo.getVersionNumber());
        pkg.setVersioncode(Integer.valueOf(packageInfo.getVersionCode()));
        pkg.setDownloadtoken(extractFileNameOnly(packageInfo.getFileName())); // 使用清理后的文件名
        pkg.setHash(packageInfo.getFileHash());
        pkg.setPackageType(packageInfo.getPackageType());

        // 设置新增的两个字段
        pkg.setNewAppver(packageInfo.getVersionNumber());
        pkg.setOldAppver(null);

        // 设置文件名相关字段（使用清理后的文件名）
        pkg.setNewFileName(extractFileNameOnly(packageInfo.getFileName()));
        pkg.setOldfileName(null);

        TaskMessage taskMessage = new TaskMessage();
        taskMessage.setTaskId(taskId);
        taskMessage.setTaskname("离线下载任务-" + packageInfo.getAppName());
        taskMessage.setPackages(Collections.singletonList(pkg));

        return taskMessage;
    }

    /**
     * 获取源文件
     */
    private File getSourceFile(String fileUrl) {
        try {
            // 处理不同协议的文件路径
            String filePath;
            if (fileUrl.startsWith("file://")) {
                filePath = fileUrl.substring(7);
            } else if (fileUrl.startsWith("http://") || fileUrl.startsWith("https://")) {
                // 如果是HTTP URL，需要下载文件
                return downloadRemoteFile(fileUrl);
            } else {
                //本地URL
                filePath = fileUrl;
            }

            File file = new File(filePath);
            if (!file.exists()) {
                throw new RuntimeException("源文件不存在: " + filePath);
            }

            return file;
        } catch (Exception e) {
            throw new RuntimeException("获取源文件失败: " + e.getMessage(), e);
        }
    }

    /**
     * 下载远程文件
     */
    private File downloadRemoteFile(String fileUrl) throws IOException {
        String tempFileName = "temp_" + System.currentTimeMillis() + "_" + extractFileNameFromUrl(fileUrl);
        File tempFile = new File(System.getProperty("java.io.tmpdir"), tempFileName);

        URL url = new URL(fileUrl);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("GET");
        connection.setConnectTimeout(30000);
        connection.setReadTimeout(30000);

        try (InputStream in = connection.getInputStream();
             FileOutputStream out = new FileOutputStream(tempFile)) {
            byte[] buffer = new byte[8192]; // 增大缓冲区提高性能
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
            }
        } finally {
            connection.disconnect();
        }

        return tempFile;
    }


    /**
     * 创建ZIP文件（资源文件和ota.dat在同一目录 - 根目录）
     */
    private File createZipFile(Long taskId, String jsonContent, File sourceFile, String originalFileName) throws IOException {
        File storageDir = new File(fileStoragePath);
        if (!storageDir.exists() && !storageDir.mkdirs()) {
            throw new IOException("无法创建文件存储目录: " + fileStoragePath);
        }

        String zipFileName = "offline_package_" + taskId + "_" + System.currentTimeMillis() + ".zip";
        File zipFile = new File(storageDir, zipFileName);

        try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile))) {
            // 添加ota.dat文件到根目录
            addZipEntry(zos, OTA_FILENAME, jsonContent.getBytes(StandardCharsets.UTF_8));

            // 添加源文件到根目录（确保只使用文件名，不包含路径）
            addFileToZip(zos, extractFileNameOnly(originalFileName), sourceFile);
        }

        return zipFile;
    }


    /**
     * 从文件名中提取纯文件名（去除路径信息）
     */
    private String extractFileNameOnly(String fileName) {
        if (StringUtils.isBlank(fileName)) {
            return "unknown_file";
        }

        // 处理Windows路径
        fileName = fileName.replace("\\", "/");

        // 提取最后的文件名部分
        int lastSlashIndex = fileName.lastIndexOf('/');
        if (lastSlashIndex != -1 && lastSlashIndex < fileName.length() - 1) {
            return fileName.substring(lastSlashIndex + 1);
        }

        return fileName;
    }



    /**
     * 添加文件到ZIP（确保只使用文件名）
     */
    private void addFileToZip(ZipOutputStream zos, String entryName, File file) throws IOException {
        // 确保entryName只包含文件名，不包含路径
        String cleanEntryName = extractFileNameOnly(entryName);

        ZipEntry entry = new ZipEntry(cleanEntryName);
        zos.putNextEntry(entry);

        try (FileInputStream fis = new FileInputStream(file)) {
            byte[] buffer = new byte[8192];
            int length;
            while ((length = fis.read(buffer)) > 0) {
                zos.write(buffer, 0, length);
            }
        }
        zos.closeEntry();
    }
    /**
     * 添加字节数组到ZIP
     */
    private void addZipEntry(ZipOutputStream zos, String entryName, byte[] content) throws IOException {
        ZipEntry entry = new ZipEntry(entryName);
        zos.putNextEntry(entry);
        zos.write(content);
        zos.closeEntry();
    }

    /**
     * 从URL中提取文件名
     */
    private String extractFileNameFromUrl(String url) {
        if (StringUtils.isBlank(url)) {
            return "unknown_file";
        }

        try {
            // 使用URI处理特殊字符
            URI uri = new URI(url);
            String path = uri.getPath();

            if (StringUtils.isNotBlank(path)) {
                int lastSlashIndex = path.lastIndexOf('/');
                if (lastSlashIndex != -1 && lastSlashIndex < path.length() - 1) {
                    return path.substring(lastSlashIndex + 1);
                }
                return path;
            }
            return "download_file";
        } catch (URISyntaxException e) {
            log.warn("URL格式不正确: {}, 使用备用方法提取文件名", url);
            // 备用方法：简单处理
            String cleanUrl = url.split("[?#]")[0]; // 去除查询参数和片段
            int lastSlashIndex = cleanUrl.lastIndexOf('/');
            if (lastSlashIndex != -1 && lastSlashIndex < cleanUrl.length() - 1) {
                return cleanUrl.substring(lastSlashIndex + 1);
            }
            return cleanUrl;
        }
    }

    @Override
    public boolean cleanupTempFile(File file) {
        if (file != null && file.exists()) {
            try {
                boolean deleted = file.delete();
                if (deleted) {
                    log.debug("临时文件已删除: {}", file.getName());
                } else {
                    log.warn("临时文件删除失败: {}", file.getName());
                }
                return deleted;
            } catch (SecurityException e) {
                log.warn("删除临时文件失败(权限不足): {}", file.getName(), e);
            }
        }
        return false;
    }
    }
